// Copyright (c) Lawrence Livermore National Security, LLC and other VisIt
// Project developers.  See the top-level LICENSE file for dates and other
// details.  No copyright assignment is required to contribute to VisIt.

// ************************************************************************* //
//                               VisWinAxes.C                                //
// ************************************************************************* //

#include <VisWinAxes.h>

#include <string>
#include <vector>

#include <vtkAxesActor2D.h>
#include <vtkVisItAxisActor2D.h>
#include <vtkProperty2D.h>
#include <vtkRenderer.h>

#include <VisWindow.h>
#include <VisWindowColleagueProxy.h>

#include <DebugStream.h>


//
// Static Function Prototypes
//

static int   LabelExponent(double, double);
static int   Digits(double, double);


using   std::string;
using   std::vector;


// ****************************************************************************
//  Method: VisWinAxes constructor
//
//  Programmer: Hank Childs
//  Creation:   June 9, 2000
//
//  Modifications:
//
//    Hank Childs, Thu Jul  6 11:34:01 PDT 2000
//    Initialized addedAxes.
//
//    Hank Childs, Wed Jul 12 09:12:02 PDT 2000
//    Initialized new data members for normalizing values and changing format.
//    Told axes that they should adjust the ticks instead of this module
//    doing it.
//
//    Kathleen Bonnell, Mon Nov 26 9:16:32 PST 2001
//    Make the axis un-pickable.
//
//    Kathleen Bonnell, Thu May 16 10:13:56 PDT 2002
//    Initialize xTitle, yTitle.
//
//    Hank Childs, Fri Sep 27 16:16:56 PDT 2002
//    Initialize more data members for the title.
//
//    Eric Brugger, Wed Nov  5 11:41:19 PST 2002
//    Added more user control over the axes and tick marks.
//
//    Eric Brugger, Fri Jan 24 09:06:18 PST 2003
//    Changed the way the font sizes for the axes labels are set.
//
//    Kathleen Bonnell, Tue Dec 16 11:47:25 PST 2003
//    Intialize autlabelscaling, userPowX,  userPowY.
//
//    Brad Whitlock, Thu Jul 28 08:47:01 PDT 2005
//    I initialized some label flags.
//
//    Brad Whitlock, Thu Mar 27 11:47:46 PDT 2008
//    Call SetUseSeparateColors(1) on the axes so the colors will come from
//    the title and label text properties.
//
//    Kathleen Biagas, Wed Jan  8 13:42:30 PST 2014
//    Utilize new vtkAxesActor2D.
//
// ****************************************************************************

VisWinAxes::VisWinAxes(VisWindowColleagueProxy &p) : VisWinColleague(p)
{
    axes = vtkAxesActor2D::New();
    lastXPow = 0;
    lastYPow = 0;

    lastXAxisDigits = 3;
    lastYAxisDigits = 3;

    addedAxes = false;

    snprintf(xTitle, 8,  "X-Axis");
    snprintf(yTitle, 8, "Y-Axis");
    unitsX[0] = '\0';
    unitsY[0] = '\0';
    powX = 0;
    powY = 0;
    SetTitle();

    autolabelScaling = true;
    userPowX = 0;
    userPowY = 0;
    userXTitle = false;
    userXUnits = false;
    userYTitle = false;
    userYUnits = false;
}


// ****************************************************************************
//  Method: VisWinAxes destructor
//
//  Programmer: Hank Childs
//  Creation:   June 9, 2000
//
// Modifications:
//    Kathleen Biagas, Wed Jan  8 13:42:30 PST 2014
//    Utilize new vtkAxesActor2D.
//
// ****************************************************************************

VisWinAxes::~VisWinAxes()
{
    if (axes != NULL)
    {
        axes->Delete();
        axes = NULL;
    }
}


// ****************************************************************************
//  Method: VisWinAxes::SetForegroundColor
//
//  Purpose:
//      Sets the foreground color of the axes.
//
//  Arguments:
//      fr       The red component (rgb) of the foreground.
//      fg       The green component (rgb) of the foreground.
//      fb       The blue component (rgb) of the foreground.
//
//  Programmer: Hank Childs
//  Creation:   June 9, 2000
//
//  Modifications:
//    Brad Whitlock, Thu Mar 27 11:09:01 PDT 2008
//    Update the title and label text attributes.
//
//    Kathleen Biagas, Wed Jan  8 13:42:30 PST 2014
//    Utilize new vtkAxesActor2D.
//
// ****************************************************************************

void
VisWinAxes::SetForegroundColor(double fr, double fg, double fb)
{
    axes->SetColor(fr, fg, fb);
    UpdateTitleTextAttributes(fr, fg, fb);
    UpdateLabelTextAttributes(fr, fg, fb);
}


// ****************************************************************************
//  Method: VisWinAxes::Start2DMode
//
//  Purpose:
//      Adds the axes to the window.  The axes are added to the background
//      renderer.
//
//  Programmer: Hank Childs
//  Creation:   June 9, 2000
//
//  Modifications:
//
//    Hank Childs, Thu Jul  6 11:34:01 PDT 2000
//    Pushed logic of adding axes to the window into its own routine.
//
//    Kathleen Bonnell, Wed May  8 14:06:50 PDT 2002
//    Set the titles here, since CurveMode uses different titles.
//
//    Kathleen Bonnell, Wed Mar 31 16:20:39 PST 2004
//    Don't set title here, allow them to be over-ridden by user-set titles.
//
// ****************************************************************************

void
VisWinAxes::Start2DMode(void)
{
    SetTitle();
    if (ShouldAddAxes())
    {
        AddAxesToWindow();
    }
}


// ****************************************************************************
//  Method: VisWinAxes::Stop2DMode
//
//  Purpose:
//      Removes the axes from the window.  The axes are removed from the
//      background renderer.
//
//  Programmer: Hank Childs
//  Creation:   June 9, 2000
//
//  Modifications:
//
//    Hank Childs, Thu Jul  6 11:34:01 PDT 2000
//    Pushed logic of removing axes from the window into its own routine.
//
// ****************************************************************************

void
VisWinAxes::Stop2DMode(void)
{
    RemoveAxesFromWindow();
}


// ****************************************************************************
//  Method: VisWinAxes::StartCurveMode
//
//  Purpose:
//      Adds the axes to the window.  The axes are added to the background
//      renderer.
//
//  Programmer: Kathleen Bonnell
//  Creation:   May 8, 2002
//
//  Modifications:
//    Kathleen Bonnell, Wed Mar 31 16:20:39 PST 2004
//    Don't set x/ytitle here, allow them to be over-ridden by user-set titles.
//
// ****************************************************************************

void
VisWinAxes::StartCurveMode(void)
{
    SetTitle();
    if (ShouldAddAxes())
    {
        AddAxesToWindow();
    }
}


// ****************************************************************************
//  Method: VisWinAxes::StopCurveMode
//
//  Purpose:
//      Removes the axes from the window.  The axes are removed from the
//      background renderer.
//
//  Programmer: Kathleen Bonnell
//  Creation:   May 8, 2002
//
// ****************************************************************************

void
VisWinAxes::StopCurveMode(void)
{
    RemoveAxesFromWindow();
}


// ****************************************************************************
//  Method: VisWinAxes::SetViewport
//
//  Purpose:
//      Changes the xAxis and yAxis to be fit with the new viewport.
//
//  Arguments:
//      vl      The left viewport in normalized device coordinates.
//      vb      The bottom viewport in normalized device coordinates.
//      vr      The right viewport in normalized device coordinates.
//      vt      The top viewport in normalized device coordinates.
//
//  Programmer: Hank Childs
//  Creation:   June 9, 2000
//
// ****************************************************************************

void
VisWinAxes::SetViewport(double vl, double vb, double vr, double vt)
{
    axes->SetCoordinateValuesFromViewport(vl, vb, vr, vt);
}


// ****************************************************************************
//  Method: VisWinAxes::AddAxesToWindow
//
//  Purpose:
//      Adds the axes to the vis window.
//
//  Programmer: Hank Childs
//  Creation:   July 6, 2000
//
//  Modifications:
//
//    Kathleen Bonnell, Fri Jul  6 14:09:00 PDT 2001
//    Added axes to foreground instead of background.
//
//    Kathleen Biagas, Wed Jan  8 13:42:30 PST 2014
//    Utilize new vtkAxesActor2D.
//
// ****************************************************************************

void
VisWinAxes::AddAxesToWindow(void)
{
    if (addedAxes)
    {
        return;
    }

    vtkRenderer *foreground = mediator.GetForeground();
    foreground->AddActor2D(axes);

    addedAxes = true;
}


// ****************************************************************************
//  Method: VisWinAxes::RemoveAxesFromWindow
//
//  Purpose:
//      Removes the axes from the vis window.
//
//  Programmer: Hank Childs
//  Creation:   July 6, 2000
//
//  Modifications:
//
//    Kathleen Bonnell, Fri Jul  6 14:09:00 PDT 2001
//    Removed axes from foreground instead of background.
//
//    Kathleen Biagas, Wed Jan  8 13:42:30 PST 2014
//    Utilize new vtkAxesActor2D.
//
// ****************************************************************************

void
VisWinAxes::RemoveAxesFromWindow(void)
{
    if (! addedAxes)
    {
        return;
    }

    vtkRenderer *foreground = mediator.GetForeground();
    foreground->RemoveActor2D(axes);

    addedAxes = false;
}


// ****************************************************************************
//  Method: VisWinAxes::ShouldAddAxes
//
//  Purpose:
//      Hides from routines that would like to add axes the logic about what
//      state the VisWindow must be in.  It should only be added if we are
//      in 2D mode and there are plots.
//
//  Returns:    true if the axes should be added to the vis window, false
//              otherwise.
//
//  Programmer: Hank Childs
//  Creation:   July 6, 2000
//
//  Modifications:
//    Kathleen Bonnell, Wed May  8 14:06:50 PDT 2002
//    Allowed for curve mode.
//
//    Mark Blair, Mon Sep 25 11:41:09 PDT 2006
//    No axes if axis annotations have already been disabled in the vis window.
//
//    Jeremy Meredith, Tue Apr 22 14:31:47 EDT 2008
//    Removed axis annotation disabling -- it was only added for a single
//    plot, and the functionality has been accomodated in a new window
//    modality supporting the correct style annotations.
//
// ****************************************************************************

bool
VisWinAxes::ShouldAddAxes(void)
{
    return ((mediator.GetMode() == WINMODE_2D ||
             mediator.GetMode() == WINMODE_CURVE) &&
             mediator.HasPlots());
}


// ****************************************************************************
//  Method: VisWinAxes::HasPlots
//
//  Purpose:
//      Receives the message from the vis window that it has plots.  This means
//      adding the axes to the vis window.
//
//  Programmer: Hank Childs
//  Creation:   July 6, 2000
//
// ****************************************************************************

void
VisWinAxes::HasPlots(void)
{
    if (ShouldAddAxes())
    {
        AddAxesToWindow();
    }
}


// ****************************************************************************
//  Method: VisWinAxes::NoPlots
//
//  Purpose:
//      Receives the message from the vis window that it has no plots.  This
//      means that we should remove the axes from the vis window.
//
//  Programmer: Hank Childs
//  Creation:   July 6, 2000
//
// ****************************************************************************

void
VisWinAxes::NoPlots(void)
{
    RemoveAxesFromWindow();
}


// ****************************************************************************
//  Method: VisWinAxes::UpdateView
//
//  Purpose:
//      Updates the axes so that they will reflect the current view.
//
//  Programmer: Hank Childs
//  Creation:   June 9, 2000
//
//  Modifications:
//
//    Hank Childs, Tue Jul 11 13:22:37 PDT 2000
//    Re-wrote function.
//
//    Kathleen Bonnell, Fri Jul  6 14:09:00 PDT 2001
//    Added functionality to set up gridlines.
//
//    Kathleen Bonnell, Fri Tue Mar 12 11:31:32 PST 2002
//    Ensure gridlines have correct length.
//
//    Kathleen Bonnell,  Wed May  8 14:06:50 PDT 2002
//    Scale y direction for winmode Curve.
//
//    Kathleen Bonnell,  Fri May 17 09:50:12 PDT 2002
//    Correct gridline length.
//
//    Eric Brugger, Fri Feb 28 11:18:28 PST 2003
//    Modified the routine to change the major tick mark labels to
//    scientific notation using vtkVisItAxisActor2D::SetMajorTickLabelScale
//    instead of modifying the range passed to vtkVisItAxisActor2D::SetRange.
//
//    Kathleen Bonnell, Thu May 15 09:46:46 PDT 2003
//    Scaling of labels can take place for 2D views well as Curve views.
//    
//    Eric Brugger, Mon Nov 24 15:55:23 PST 2003
//    I removed the code to adjust the range using the scale factor since
//    the values returned from GetRange already accounted for it.  I also
//    added code to set the axis orientation angle to handle degenerate
//    viewports.
//
//    Kathleen Bonnell, Thu Apr 29 16:54:44 PDT 2004
//    Initialize min_x, etc, to avoid UMR's.
//
//    Kathleen Bonnell, Thu Mar 22 19:24:21 PDT 2007
//    Send the log-scaling mode to the axis if Curve mode.
//
//    Kathleen Bonnell, Thu Mar 29 10:30:41 PDT 2007
//    Call AdjustLabelFormatForLogScale.
//
//    Kathleen Bonnell, Wed May  9 11:01:47 PDT 2007
//    Account for 2D log scaling.
//
//    Kathleen Bonnell,Tue May 15 08:52:02 PDT 2007
//    Remove early termination if not 2D or Curve, was causing regression
//    failures.
//
//    Kathleen Biagas, Wed Jan  8 13:42:30 PST 2014
//    Utilize new vtkAxesActor2D.  Moved gridline-lenght setting logic to the
//    new actor, as it wasn't working correctly in this spot.
//
// ****************************************************************************

void
VisWinAxes::UpdateView(void)
{
    double  min_x = 0., max_x = 0., min_y = 0., max_y = 0.;
    GetRange(min_x, max_x, min_y, max_y);

    //
    // If the range or values are too big or too small, put them in scientific
    // notation.
    //
    AdjustValues(min_x, max_x, min_y, max_y);
    AdjustRange(min_x, max_x, min_y, max_y);

    axes->SetXRange(min_x, max_x);
    axes->SetYRange(min_y, max_y);

    if (powY != 0)
        axes->SetYLabelScale(1./pow(10., powY));
    else
        axes->SetYLabelScale(1.);
    if (powX != 0)
        axes->SetXLabelScale(1./pow(10., powX));
    else
        axes->SetXLabelScale(1.);

    bool scaleMode[2] = {false, false};

    VisWindow *vw = mediator;
    if (vw->GetWindowMode() == WINMODE_CURVE)
    {
        const avtViewCurve viewCurve = vw->GetViewCurve();
        scaleMode[0] = viewCurve.domainScale == LOG;
        scaleMode[1] = viewCurve.rangeScale == LOG;
    }
    else if (vw->GetWindowMode() == WINMODE_2D)
    {
        const avtView2D view2D = vw->GetView2D();
        scaleMode[0] = view2D.xScale == LOG;
        scaleMode[1] = view2D.yScale == LOG;
    }
    axes->SetXLogScaling((int)scaleMode[0]);
    axes->SetYLogScaling((int)scaleMode[1]);
    if (scaleMode[0] || scaleMode[1])
    {
        AdjustLabelFormatForLogScale(min_x, max_x, min_y, max_y, scaleMode);
    }
}


// ****************************************************************************
//  Method: VisWinAxes::UpdatePlotList
//
//  Purpose:
//      Decides what the units are for the X and Y directions.
//
//  Programmer: Hank Childs
//  Creation:   September 27, 2002
//
//  Modifications:
//
//    Hank Childs, Wed Oct 15 21:44:19 PDT 2003
//    Add labels.
//
//    Kathleen Bonnell, Tue Mar 23 08:57:31 PST 2004
//    Allow labels from DataAtts to be set for Curve window, too.
//
//    Brad Whitlock, Thu Jul 28 08:48:35 PDT 2005
//    Added code that allows the user to override the titles and units.
//
// ****************************************************************************

void
VisWinAxes::UpdatePlotList(vector<avtActor_p> &list)
{
    int nActors = list.size();
    string x, y;
    string lx, ly;
    for (int i = 0 ; i < nActors ; i++)
    {
        avtDataAttributes &atts = 
                             list[i]->GetBehavior()->GetInfo().GetAttributes();

        // Last one in is the winner.
        if (atts.GetXUnits() != "")
        {
            x = atts.GetXUnits();
        }
        if (atts.GetYUnits() != "")
        {
            y = atts.GetYUnits();
        }
        if ((atts.GetXLabel() != "") && (atts.GetXLabel() != "X-Axis"))
        {
            lx = atts.GetXLabel();
        }
        if ((atts.GetYLabel() != "") && (atts.GetYLabel() != "Y-Axis"))
        {
            ly = atts.GetYLabel();
        }
    }

    if(!userXUnits)
        snprintf(unitsX, 256, "%s", x.c_str());
    if(!userYUnits)
        snprintf(unitsY, 256, "%s", y.c_str());
    if(!userXTitle)
    {
        if (lx != "")
            snprintf(xTitle, 256, "%s", lx.c_str());
        else
            strcpy(xTitle, "X-Axis");
    }
    if(!userYTitle)
    {
        if (ly != "")
            snprintf(yTitle, 256, "%s", ly.c_str());
        else
            strcpy(yTitle, "Y-Axis");
    }

    SetTitle();
}


// ****************************************************************************
//  Method: VisWinAxes::GetRange
//
//  Purpose:
//      Gets the range of the viewport.
//
//  Arguments:
//      min_x        Set to be the minimum x value.
//      max_x        Set to be the maximum x value.
//      min_y        Set to be the minimum y value.
//      max_y        Set to be the maximum y value.
//
//  Programmer: Hank Childs
//  Creation:   July 11, 2000
//
//  Modifications:
//    Eric Brugger, Mon Nov 24 15:55:23 PST 2003
//    I rewrote the routine to get the range from the curve or 2d view from
//    the vis window.
//
//    Hank Childs, Fri Jun  9 14:43:27 PDT 2006
//    Add missing cases for the switch statement.
//
//    Kathleen Bonnell, Tue Nov 20 15:12:57 PST 2007
//    Removed debug message for default case.
// 
// ****************************************************************************

void
VisWinAxes::GetRange(double &min_x, double &max_x, double &min_y, double &max_y)
{
    VisWindow *vw = mediator;

    switch (vw->GetWindowMode())
    {
      case WINMODE_2D:
        {
        const avtView2D view2D = vw->GetView2D();
        min_x = view2D.window[0];
        max_x = view2D.window[1];
        min_y = view2D.window[2];
        max_y = view2D.window[3];
        }
        break;
      case WINMODE_CURVE: 
        {
        const avtViewCurve viewCurve = vw->GetViewCurve();
        min_x = viewCurve.domain[0];
        max_x = viewCurve.domain[1];
        min_y = viewCurve.range[0];
        max_y = viewCurve.range[1];
        }
        break;
      default:
        break;
    }
}


// ****************************************************************************
//  Method: VisWinAxes::AdjustValues
//
//  Purpose:
//      If the range or values is too big or too small, put them in scientific
//      notation and changes the labels.
//
//  Arguments:
//      min_x    The minimum x value.  This may be put into scientific form.
//      max_x    The maximum x value.  This may be put into scientific form.
//      min_y    The minimum y value.  This may be put into scientific form.
//      max_y    The maximum y value.  This may be put into scientific form.
//
//  Note:       This code is partially stolen from old MeshTV code,
//              /meshtvx/toolkit/plotgrid.c, axlab[x|y].
//
//  Programmer: Hank Childs
//  Creation:   July 11, 2000
//
//  Modifications:
//    Kathleen Bonnell, Wed May  8 14:06:50 PDT 2002
//    Use members to set titles, as they change depending on winmode.
//
//    Hank Childs, Fri Sep 27 17:46:30 PDT 2002
//    Do the actual setting of the title in a separate routine.
//
//    Eric Brugger, Fri Feb 28 11:18:28 PST 2003
//    Modified the routine to leave the input values unchanged.
//
//    Kathleen Bonnell, Tue Dec 16 11:47:25 PST 2003
//    Utilize user-set exponents if autolabelscaling is off.
//
// ****************************************************************************

void
VisWinAxes::AdjustValues(double min_x, double max_x, double min_y, double max_y)
{
    int curPowX;
    int curPowY;
    if (autolabelScaling)
    {
        curPowX = LabelExponent(min_x, max_x);
        curPowY = LabelExponent(min_y, max_y);
    }
    else
    {
        curPowX = userPowX;
        curPowY = userPowY;
    }

    if (curPowX != powX)
    {
        powX = curPowX;
        SetTitle();
    }

    if (curPowY != powY)
    {
        powY = curPowY;
        SetTitle();
    }
}


// ****************************************************************************
//  Method: VisWinAxes::AdjustRange
//
//  Purpose:
//      If the range is small, adjust the precision of the values displayed.
//
//  Arguments:
//      min_x   The minimum x value.
//      max_x   The maximum x value.
//      min_y   The minimum y value.
//      max_y   The maximum y value.
//
//  Programmer: Hank Childs
//  Creation:   July 11, 2000
//
//  Modifications:
//    Eric Brugger, Fri Feb 28 11:18:28 PST 2003
//    Modified the routine to correct the range values using powX and powY
//    since they are not being corrected before calling this routine.
//
//    Kathleen Biagas, Wed Jan  8 13:42:30 PST 2014
//    Utilize new vtkAxesActor2D.
//
// ****************************************************************************

void
VisWinAxes::AdjustRange(double min_x, double max_x, double min_y, double max_y)
{
    if (powX != 0)
    {
        min_x /= pow(10., powX);
        max_x /= pow(10., powX);
    }
    int xAxisDigits = Digits(min_x, max_x);
    if (xAxisDigits != lastXAxisDigits)
    {
        char  format[16];
        snprintf(format, 16, "%%.%df", xAxisDigits);
        axes->SetXLabelFormatString(format);
        lastXAxisDigits = xAxisDigits;
    }

    if (powY != 0)
    {
        min_y /= pow(10., powY);
        max_y /= pow(10., powY);
    }
    int yAxisDigits = Digits(min_y, max_y);
    if (yAxisDigits != lastYAxisDigits)
    {
        char  format[16];
        snprintf(format, 16, "%%.%df", yAxisDigits);
        axes->SetYLabelFormatString(format);
        lastYAxisDigits = yAxisDigits;
    }
}


// ****************************************************************************
//  Function: Digits
//
//  Purpose:
//      Determines the appropriate number of digits for a given range.
//
//  Arguments:
//      min    The minimum value in the range.
//      max    The maximum value in the range.
//
//  Returns:   The appropriate number of digits.
//
//  Programmer: Hank Childs
//  Creation:   July 11, 2000
//
//  Modifications:
//
//    Hank Childs, Tue Sep 18 10:41:30 PDT 2001
//    Casted to get rid of warning.
//
//    Akira Haddox, Wed Jul 16 16:48:20 PDT 2003
//    Added special case for when range is zero.
//
//    Mark C. Miller, Wed Jul 27 08:39:38 PDT 2005
//    Moved test for non-positive range to *before* call to log10
//
// ****************************************************************************

int
Digits(double min, double max)
{
    double  range = max - min;
    double  pow10 = -5.0;
    if (range > 0)
        pow10 = log10(range);

    int    ipow10  = (int)floor(pow10);

    int    digitsPastDecimal = -ipow10;

    if (digitsPastDecimal < 0)
    {
        //
        // The range is more than 10, we don't need to worry about decimals.
        //
        digitsPastDecimal = 0;
    }
    else
    {
        //
        // We want one more than the range since there is more than one
        // tick per decade.
        //
        digitsPastDecimal++;

        //
        // Anything more than 5 is just noise.  (and probably 5 is noise with
        // floating point is the part before the decimal is big).
        //
        if (digitsPastDecimal > 5)
        {
            digitsPastDecimal = 5;
        }
    }

    return digitsPastDecimal;
}


// ****************************************************************************
//  Function: LabelExponent
//
//  Purpose:
//      Determines the proper exponent for the min and max values.
//
//  Arguments:
//      min     The minimum value along a certain axis.
//      max     The maximum value along a certain axis.
//
//  Note:       This code is mostly stolen from old MeshTV code,
//              /meshtvx/toolkit/plotgrid.c, axlab_format.
//
//  Programmer: Hank Childs
//  Creation:   July 11, 2000
//
//  Modifications:
//
//    Hank Childs, Tue Sep 18 10:41:30 PDT 2001
//    Casted to get rid of compiler warning.
//
//    Jeremy Meredith, Fri Aug 14 09:42:43 EDT 2009
//    Fix case where range==0.
//
// ****************************************************************************

int
LabelExponent(double min, double max)
{
    //
    // Determine power of 10 to scale axis labels to.
    //
    double range = (fabs(min) > fabs(max) ? fabs(min) : fabs(max));

    double pow10 = -5.0;
    if (range > 0)
        pow10 = log10(range);

    //
    // Cutoffs for using scientific notation.
    //
    static double  eformat_cut_min = -1.5;
    static double  eformat_cut_max =  3.0;
    static double  cut_min = pow(10., eformat_cut_min);
    static double  cut_max = pow(10., eformat_cut_max);
    double ipow10;
    if (range < cut_min || range > cut_max)
    {
        //
        // We are going to use scientific notation and round the exponents to
        // the nearest multiple of three.
        //
        ipow10 = (floor(floor(pow10)/3.))*3;
    }
    else
    {
        ipow10 = 0.;
    }

    return (int)ipow10;
}


// ****************************************************************************
//  Method: VisWinAxes::SetXTickVisibility
//
//  Purpose:
//    Sets the visibility of x-axis ticks.
//
//  Arguments:
//    xVis       The visibility of the x-axis ticks.
//    xLabelsVis The visibility of the x-axis labels.
//
//  Programmer: Kathleen Bonnell
//  Creation:   June 18, 2001.
//
// Modifications:
//    Kathleen Biagas, Wed Jan  8 13:42:30 PST 2014
//    Utilize new vtkAxesActor2D.
//
// ****************************************************************************

void
VisWinAxes::SetXTickVisibility(int xVis, int xLabelsVis)
{
    axes->SetXMinorTickVisibility(xVis);

    // Major ticks dependent upon visibility of labels.
    axes->SetXMajorTickVisibility(xVis || xLabelsVis);
}


// ****************************************************************************
//  Method: VisWinAxes::SetYTickVisibility
//
//  Purpose:
//      Sets the visibility of y-axis ticks.
//
//  Arguments:
//    yVis       The visibility of the y-axis ticks.
//    yLabelsVis The visibility of the y-axis labels.
//
//  Programmer: Kathleen Bonnell
//  Creation:   June 18, 2001
//
// Modifications:
//    Kathleen Biagas, Wed Jan  8 13:42:30 PST 2014
//    Utilize new vtkAxesActor2D.
//
// ****************************************************************************

void
VisWinAxes::SetYTickVisibility(int yVis, int yLabelsVis)
{
    axes->SetYMinorTickVisibility(yVis);
    // Major ticks dependent upon visibility of labels.
    axes->SetYMajorTickVisibility(yVis || yLabelsVis);
}


// ****************************************************************************
//  Method: VisWinAxes::SetLabelsVisibility
//
//  Purpose:
//      Sets the visibility of x-axis and y-axis labels.
//
//  Arguments:
//      x-vis     The visibility of the x-axis labels.
//      y-vis     The visibility of the y-axis labels.
//
//  Programmer: Kathleen Bonnell
//  Creation:   June 18, 2001
//
//  Modifications:
//    Eric Brugger, Wed Nov  5 11:41:19 PST 2002
//    Removed the control of the title visibility out of this method into
//    its own method.
//
//    Kathleen Biagas, Wed Jan  8 13:42:30 PST 2014
//    Utilize new vtkAxesActor2D.
//
// ****************************************************************************

void
VisWinAxes::SetLabelsVisibility(int xVis, int yVis)
{
    axes->SetXLabelVisibility(xVis);
    axes->SetYLabelVisibility(yVis);
}


// ****************************************************************************
//  Method: VisWinAxes::SetTitleVisibility
//
//  Purpose:
//      Sets the visibility of x-axis and y-axis titles.
//
//  Arguments:
//      x-vis     The visibility of the x-axis titles.
//      y-vis     The visibility of the y-axis titles.
//
//  Programmer: Eric Brugger
//  Creation:   November 6, 2002
//
// Modifications:
//    Kathleen Biagas, Wed Jan  8 13:42:30 PST 2014
//    Utilize new vtkAxesActor2D.
//
// ****************************************************************************

void
VisWinAxes::SetTitleVisibility(int xVis, int yVis)
{
    axes->SetXTitleVisibility(xVis);
    axes->SetYTitleVisibility(yVis);
}


// ****************************************************************************
//  Method: VisWinAxes::SetVisibility
//
//  Purpose:
//      Sets the visibility of this colleague.
//
//  Arguments:
//      vis     The visibility of this colleague.
//
//  Programmer: Kathleen Bonnell
//  Creation:   June 18, 2001
//
// Modifications:
//    Kathleen Biagas, Wed Jan  8 13:42:30 PST 2014
//    Utilize new vtkAxesActor2D.
//
// ****************************************************************************

void
VisWinAxes::SetVisibility(int vis)
{
    axes->SetVisibility(vis);
}


// ****************************************************************************
//  Method: VisWinAxes::SetTickLocation
//
//  Purpose:
//      Sets the location of the ticks.
//
//  Arguments:
//      loc     The location of the ticks.
//
//  Programmer: Kathleen Bonnell
//  Creation:   June 27, 2001
//
// Modifications:
//    Kathleen Biagas, Wed Jan  8 13:42:30 PST 2014
//    Utilize new vtkAxesActor2D.
//
// ****************************************************************************

void
VisWinAxes::SetTickLocation(int loc)
{
    axes->SetTickLocation(loc);
}


// ****************************************************************************
//  Method: VisWinAxes::SetXGridVisibility
//
//  Purpose:
//      Sets the visibility of x-axis gridlines.
//
//  Arguments:
//      vis     The visibility of the gridlines.
//
//  Programmer: Kathleen Bonnell
//  Creation:   July 6, 2001
//
// Modifications:
//    Kathleen Biagas, Wed Jan  8 13:42:30 PST 2014
//    Utilize new vtkAxesActor2D.
//
// ****************************************************************************

void
VisWinAxes::SetXGridVisibility(int vis)
{
    axes->SetXGridVisibility(vis);
}


// ****************************************************************************
//  Method: VisWinAxes::SetYGridVisibility
//
//  Purpose:
//      Sets the visibility of y-axis gridlines.
//
//  Arguments:
//      vis     The visibility of the gridlines.
//
//  Programmer: Kathleen Bonnell
//  Creation:   July 6, 2001
//
// Modifications:
//    Kathleen Biagas, Wed Jan  8 13:42:30 PST 2014
//    Utilize new vtkAxesActor2D.
//
// ****************************************************************************

void
VisWinAxes::SetYGridVisibility(int vis)
{
    axes->SetYGridVisibility(vis);
}


// ****************************************************************************
//  Method: VisWinAxes::SetAutoSetTicks
//
//  Purpose:
//      Sets the flag which specifies if the ticks should be automatically
//      selected or user specified.
//
//  Arguments:
//      autoSetTicks The flag indicating if the ticks should be selected
//                   automatically.
//
//  Programmer: Eric Brugger
//  Creation:   November 5, 2002
//
// Modifications:
//    Kathleen Biagas, Wed Jan  8 13:42:30 PST 2014
//    Utilize new vtkAxesActor2D.
//
// ****************************************************************************

void
VisWinAxes::SetAutoSetTicks(int autoSetTicks)
{
    axes->SetAutoAdjustLabels(autoSetTicks);
}


// ****************************************************************************
//  Method: VisWinAxes::SetMajorTickMinimum
//
//  Purpose:
//      Sets the minimum values for the major tick marks.
//
//  Arguments:
//      xMajorMinimum The minimum value for the x major tick marks.
//      yMajorMinimum The minimum value for the y major tick marks.
//
//  Programmer: Eric Brugger
//  Creation:   November 5, 2002
//
// Modifications:
//    Kathleen Biagas, Wed Jan  8 13:42:30 PST 2014
//    Utilize new vtkAxesActor2D.
//
// ****************************************************************************

void
VisWinAxes::SetMajorTickMinimum(double xMajorMinimum, double yMajorMinimum)
{
    axes->SetXMajorTickMinimum(xMajorMinimum);
    axes->SetYMajorTickMinimum(yMajorMinimum);
}


// ****************************************************************************
//  Method: VisWinAxes::SetMajorTickMaximum
//
//  Purpose:
//      Sets the maximum values for the major tick marks.
//
//  Arguments:
//      xMajorMaximum The maximum value for the x major tick marks.
//      yMajorMaximum The maximum value for the y major tick marks.
//
//  Programmer: Eric Brugger
//  Creation:   November 5, 2002
//
// Modifications:
//    Kathleen Biagas, Wed Jan  8 13:42:30 PST 2014
//    Utilize new vtkAxesActor2D.
//
// ****************************************************************************

void
VisWinAxes::SetMajorTickMaximum(double xMajorMaximum, double yMajorMaximum)
{
    axes->SetXMajorTickMaximum(xMajorMaximum);
    axes->SetYMajorTickMaximum(yMajorMaximum);
}


// ****************************************************************************
//  Method: VisWinAxes::SetMajorTickSpacing
//
//  Purpose:
//      Sets the spacing for the major tick marks.
//
//  Arguments:
//      xMajorSpacing The spacing for the x major tick marks.
//      yMajorSpacing The spacing for the y major tick marks.
//
//  Programmer: Eric Brugger
//  Creation:   November 5, 2002
//
// Modifications:
//    Kathleen Biagas, Wed Jan  8 13:42:30 PST 2014
//    Utilize new vtkAxesActor2D.
//
// ****************************************************************************

void
VisWinAxes::SetMajorTickSpacing(double xMajorSpacing, double yMajorSpacing)
{
    axes->SetXMajorTickSpacing(xMajorSpacing);
    axes->SetYMajorTickSpacing(yMajorSpacing);
}


// ****************************************************************************
//  Method: VisWinAxes::SetMinorTickSpacing
//
//  Purpose:
//      Sets the spacing for the minor tick marks.
//
//  Arguments:
//      xMinorSpacing The spacing for the x minor tick marks.
//      yMinorSpacing The spacing for the y minor tick marks.
//
//  Programmer: Eric Brugger
//  Creation:   November 5, 2002
//
// Modifications:
//    Kathleen Biagas, Wed Jan  8 13:42:30 PST 2014
//    Utilize new vtkAxesActor2D.
//
// ****************************************************************************

void
VisWinAxes::SetMinorTickSpacing(double xMinorSpacing, double yMinorSpacing)
{
    axes->SetXMinorTickSpacing(xMinorSpacing);
    axes->SetYMinorTickSpacing(yMinorSpacing);
}


// ****************************************************************************
//  Method: VisWinAxes::SetXLabelFontHeight
//
//  Purpose:
//      Sets the label font height for the x axis.
//
//  Arguments:
//      height  The font height.
//
//  Programmer: Eric Brugger
//  Creation:   January 24, 2003
//
// Modifications:
//    Kathleen Biagas, Wed Jan  8 13:42:30 PST 2014
//    Utilize new vtkAxesActor2D.
//
// ****************************************************************************

void
VisWinAxes::SetXLabelFontHeight(double height)
{
    axes->SetXLabelFontHeight(height);
}


// ****************************************************************************
//  Method: VisWinAxes::SetYLabelFontHeight
//
//  Purpose:
//      Sets the label font height for the y axis.
//
//  Arguments:
//      height  The font height.
//
//  Programmer: Eric Brugger
//  Creation:   January 24, 2003
//
// Modifications:
//    Kathleen Biagas, Wed Jan  8 13:42:30 PST 2014
//    Utilize new vtkAxesActor2D.
//
// ****************************************************************************

void
VisWinAxes::SetYLabelFontHeight(double height)
{
    axes->SetYLabelFontHeight(height);
}


// ****************************************************************************
//  Method: VisWinAxes::SetXTitleFontHeight
//
//  Purpose:
//      Sets the title font height for the x axis.
//
//  Arguments:
//      height  The font height.
//
//  Programmer: Eric Brugger
//  Creation:   January 24, 2003
//
// Modifications:
//    Kathleen Biagas, Wed Jan  8 13:42:30 PST 2014
//    Utilize new vtkAxesActor2D.
//
// ****************************************************************************

void
VisWinAxes::SetXTitleFontHeight(double height)
{
    axes->SetXTitleFontHeight(height);
}


// ****************************************************************************
//  Method: VisWinAxes::SetYTitleFontHeight
//
//  Purpose:
//      Sets the title font height for the y axis.
//
//  Arguments:
//      height  The font height.
//
//  Programmer: Eric Brugger
//  Creation:   January 24, 2003
//
// Modifications:
//    Kathleen Biagas, Wed Jan  8 13:42:30 PST 2014
//    Utilize new vtkAxesActor2D.
//
// ****************************************************************************

void
VisWinAxes::SetYTitleFontHeight(double height)
{
    axes->SetYTitleFontHeight(height);
}


// ****************************************************************************
//  Method: VisWinAxes::SetLineWidth
//
//  Purpose:
//      Sets the line width for the axes.
//
//  Arguments:
//      width   The line width.
//
//  Programmer: Eric Brugger
//  Creation:   June 25, 2003
//
// Modifications:
//    Kathleen Biagas, Wed Jan  8 13:42:30 PST 2014
//    Utilize new vtkAxesActor2D.
//
// ****************************************************************************

void
VisWinAxes::SetLineWidth(int width)
{
    axes->SetLineWidth(width);
}


// ****************************************************************************
//  Method: VisWinAxes::SetTitle
//
//  Purpose:
//      Sets the title, including exponent and units.
//
//  Programmer: Hank Childs
//  Creation:   September 27, 2002
//
//  Modifications:
//    Eric Brugger, Wed Nov  5 11:41:19 PST 2002
//    Added the font scale factor.
//
//    Eric Brugger, Fri Jan 24 09:06:18 PST 2003
//    Removed the setting of the font size since it no longer depends on
//    the title.
//
//    Eric Brugger, Fri Feb 28 11:18:28 PST 2003
//    Changed the printing of the y axis label to look nicer when displaying
//    scientific notation and units.
//
//    Kathleen Bonnell, Tue Dec 16 11:47:25 PST 2003 
//    Use '10e' insted of just 'e' when diplaying scientific notation. 
//
//    Kathleen Bonnell, Tue Jul 20 11:44:36 PDT 2004 
//    Use 'x10^' insted of '10e' when diplaying scientific notation. 
//
//    Brad Whitlock, Wed Aug 31 14:35:43 PDT 2011
//    Remove end of line for Y-axis units.
//
//    Kathleen Biagas, Wed Jan  8 13:42:30 PST 2014
//    Utilize new vtkAxesActor2D.
//
// ****************************************************************************

void
VisWinAxes::SetTitle(void)
{
    char buffer[1024];
    if (powX == 0)
    {
        if (unitsX[0] == '\0')
            snprintf(buffer, 1024, "%s", xTitle);
        else
            snprintf(buffer, 1024, "%s (%s)", xTitle, unitsX);
    }
    else
    {
        if (unitsX[0] == '\0')
            snprintf(buffer, 1024, "%s (x10^%d)", xTitle, powX);
        else
            snprintf(buffer, 1024, "%s (x10^%d %s)", xTitle, powX, unitsX);
    }
    axes->SetXTitle(buffer);

    if (powY == 0)
    {
        if (unitsY[0] == '\0')
            snprintf(buffer, 1024, "%s", yTitle);
        else
            snprintf(buffer, 1024, "%s (%s)", yTitle, unitsY);
    }
    else
    {
        if (unitsY[0] == '\0')
            snprintf(buffer, 1024, "%s (x10^%d)", yTitle, powY);
        else
            snprintf(buffer, 1024, " %s (x10^%d %s)", yTitle, powY, unitsY);
    }
    axes->SetYTitle(buffer);
}


// ****************************************************************************
//  Method: VisWinAxes::SetLabelScaling
//
//  Purpose:
//      Sets autolabelscaling and the exponents to be used with each axis.
//
//  Programmer: Kathleen Bonnell
//  Creation:   December 16, 2003
//
//  Modifications:
//
// ****************************************************************************

void
VisWinAxes::SetLabelScaling(bool autoscale, int upowX, int upowY)
{
    autolabelScaling = autoscale;
    userPowX = upowX;
    userPowY = upowY;
}

// ****************************************************************************
// Method: VisWinAxes::SetTitle
//
// Purpose: 
//   Sets the title for an axis, overriding any title that comes from the plot.
//
// Arguments:
//   title   : The new title.
//   userSet : Whether the title is user-set.
//
// Programmer: Brad Whitlock
// Creation:   Thu Jul 28 10:58:41 PDT 2005
//
// Modifications:
//
// ****************************************************************************

void
VisWinAxes::SetXTitle(const string &title, bool userSet)
{
    if(userSet)
        snprintf(xTitle, 256, "%s", title.c_str());
    userXTitle = userSet;
}

void
VisWinAxes::SetYTitle(const string &title, bool userSet)
{
    if(userSet)
        snprintf(yTitle, 256, "%s", title.c_str());
    userYTitle = userSet;
}

// ****************************************************************************
// Method: VisWinAxes::SetUnits
//
// Purpose: 
//   Sets the units for an axis, overriding any units that comes from the plot.
//
// Arguments:
//   units   : The new units.
//   userSet : Whether the title is user-set.
//
// Programmer: Brad Whitlock
// Creation:   Thu Jul 28 10:58:41 PDT 2005
//
// Modifications:
//
// ****************************************************************************

void
VisWinAxes::SetXUnits(const string &units, bool userSet)
{
    if(userSet)
        snprintf(unitsX, 256, "%s", units.c_str());
    userXUnits = userSet;
}

void
VisWinAxes::SetYUnits(const string &units, bool userSet)
{
    if(userSet)
        snprintf(unitsY, 256, "%s", units.c_str());
    userYUnits = userSet;
}

// ****************************************************************************
// Method: VisWinAxes::AdjustLabelFormatForLogScale
//
// Purpose:
//   Performs some of the same adjustments as AdjustLabels and AdjustRange,
//   without the side-effect of setting ivars.  Does, however change
//   the axis label formats.
//
// Arguments:
//   min_x     The minimum x value. (already log-scaled)
//   max_x     The maximum x value. (already log-scaled)
//   min_y     The minimum y value. (already log-scaled)
//   max_y     The maximum y value. (already log-scaled)
//
// Programmer: Kathleen Bonnell
// Creation:   March 29, 2007
//
// Modifications:
//   Kathleen Bonnell, Wed Apr  4 17:04:54 PDT 2007
//   Modified to handle situations where scientific notation is required.
//
//   Kathleen Bonnell, Thu Apr  5 15:03:40 PDT 2007
//   Fix setting of y-axis info, also changed to use new LogLabelFormat so
//   that the switching between Log-Linear the labels have the right format.
//
//   Kathleen Bonnell, Wed May  9 11:01:47 PDT 2007
//   Account for 2D log scaling, added bool args.
//
//   Kathleen Biagas, Wed Jan  8 13:42:30 PST 2014
//   Utilize new vtkAxesActor2D.
//
//   Eric Brugger, Fri Dec 17 09:34:23 PST 2021
//   Modify the logic so that no digits are displayed after the decimal
//   point when using e notation. Fix the logic for specifying the number
//   of digits after the decimal point when using f notation.
//
// ****************************************************************************

void
VisWinAxes::AdjustLabelFormatForLogScale(
    double min_x, double max_x, double min_y, double max_y, bool sm[2])
{
    VisWindow *vw = mediator;
    if (vw->GetWindowMode() != WINMODE_CURVE &&
        vw->GetWindowMode() != WINMODE_2D)
        return;

    //
    // The labels will be non-log-scaled, so must convert min & max here
    // to get the correct range that will be used.
    //
    if (sm[0])
    {
        int xAxisDigits = 0;
        if (min_x < 0.)
        {
            xAxisDigits = int(fabs(ceil(min_x)));
            //
            // Add an extra digit if the range is within a decade.
            //
            if (ceil(min_x) == ceil(max_x))
                xAxisDigits++;
        }

        double sorted_minx = min_x < max_x ? min_x : max_x;
        double sorted_maxx = min_x > max_x ? min_x : max_x;

        char  format[16];
        if (sorted_minx <= -4 || sorted_maxx >= 4)
            strncpy(format, "%5.0e", 16);
        else
            snprintf(format, 16, "%%.%df", xAxisDigits);
        axes->SetXLogLabelFormatString(format);
    }
    if (sm[1])
    {
        int yAxisDigits = 0;
        if (min_y < 0.)
        {
            yAxisDigits = int(fabs(ceil(min_y)));
            //
            // Add an extra digit if the range is within a decade.
            //
            if (ceil(min_y) == ceil(max_y))
                yAxisDigits++;
        }

        double sorted_miny = min_y < max_y ? min_y : max_y;
        double sorted_maxy = min_y > max_y ? min_y : max_y;

        char  format[16];
        if (sorted_miny <= -4 || sorted_maxy >= 4)
            strncpy(format, "%5.0e", 16);
        else
            snprintf(format, 16, "%%.%df", yAxisDigits);
        axes->SetYLogLabelFormatString(format);
    }
}

// ****************************************************************************
// Method: VisWinAxes::SetTitleTextAttributes
//
// Purpose:
//   Sets the title properties for all axes.
//
// Arguments:
//   x : The text properties for the X axis title.
//   y : The text properties for the Y axis title.
//
// Returns:
//
// Note:
//
// Programmer: Brad Whitlock
// Creation:   Wed Mar 26 14:17:25 PDT 2008
//
// Modifications:
//
// ****************************************************************************

void
VisWinAxes::SetTitleTextAttributes(
    const VisWinTextAttributes &x,
    const VisWinTextAttributes &y)
{
    titleTextAttributes[0] = x;
    titleTextAttributes[1] = y;

    double rgb[3];
    mediator.GetForegroundColor(rgb);
    UpdateTitleTextAttributes(rgb[0], rgb[1], rgb[2]);
}

// ****************************************************************************
// Method: VisWinAxes::SetLabelTextAttributes
//
// Purpose:
//   Sets the label properties for all axes.
//
// Arguments:
//   xAxis : The text properties for the X axis labels.
//   yAxis : The text properties for the Y axis labels.
//
// Returns:
//
// Note:
//
// Programmer: Brad Whitlock
// Creation:   Wed Mar 26 14:17:25 PDT 2008
//
// Modifications:
//
// ****************************************************************************

void
VisWinAxes::SetLabelTextAttributes(
    const VisWinTextAttributes &x,
    const VisWinTextAttributes &y)
{
    labelTextAttributes[0] = x;
    labelTextAttributes[1] = y;

    double rgb[3];
    mediator.GetForegroundColor(rgb);
    UpdateLabelTextAttributes(rgb[0], rgb[1], rgb[2]);
}

// ****************************************************************************
// Method: VisWinAxes::UpdateTitleTextAttributes
//
// Purpose:
//   Sets the title text properties into the axes.
//
// Arguments:
//   fr, fg, fb : Red, green, blue color components [0.,1].
//
// Returns:
//
// Note:
//
// Programmer: Brad Whitlock
// Creation:   Wed Mar 26 14:18:45 PDT 2008
//
// Modifications:
//    Kathleen Biagas, Wed Jan  8 13:42:30 PST 2014
//    Utilize new vtkAxesActor2D.
//
// ****************************************************************************

void
VisWinAxes::UpdateTitleTextAttributes(double fr, double fg, double fb)
{
    vtkTextProperty *titles[2];
    titles[0] = axes->GetXTitleTextProperty();
    titles[1] = axes->GetYTitleTextProperty();

    for(int i = 0; i < 2; ++i)
    {
        // Set the colors
        if(titleTextAttributes[i].useForegroundColor)
            titles[i]->SetColor(fr, fg, fb);
        else
        {
            titles[i]->SetColor(
                titleTextAttributes[i].color[0],
                titleTextAttributes[i].color[1],
                titleTextAttributes[i].color[2]);
        }

        titles[i]->SetFontFamily((int)titleTextAttributes[i].font);
        titles[i]->SetBold(titleTextAttributes[i].bold?1:0);
        titles[i]->SetItalic(titleTextAttributes[i].italic?1:0);

        // Pass the opacity in the line offset.
        titles[i]->SetLineOffset(titleTextAttributes[i].color[3]);
    }
}

// ****************************************************************************
// Method: VisWinAxes::UpdateLabelTextAttributes
//
// Purpose:
//   Sets the label text properties into the axes.
//
// Arguments:
//   fr, fg, fb : Red, green, blue color components [0.,1].
//
// Returns:
//
// Note:
//
// Programmer: Brad Whitlock
// Creation:   Wed Mar 26 14:18:45 PDT 2008
//
// Modifications:
//    Kathleen Biagas, Wed Jan  8 13:42:30 PST 2014
//    Utilize new vtkAxesActor2D.
//
// ****************************************************************************

void
VisWinAxes::UpdateLabelTextAttributes(double fr, double fg, double fb)
{
    vtkTextProperty *labels[2];
    labels[0] = axes->GetXLabelTextProperty();
    labels[1] = axes->GetYLabelTextProperty();

    for(int i = 0; i < 2; ++i)
    {
        // Set the colors
        if(labelTextAttributes[i].useForegroundColor)
            labels[i]->SetColor(fr, fg, fb);
        else
        {
            labels[i]->SetColor(
                labelTextAttributes[i].color[0],
                labelTextAttributes[i].color[1],
                labelTextAttributes[i].color[2]);
        }

        labels[i]->SetFontFamily((int)labelTextAttributes[i].font);
        labels[i]->SetBold(labelTextAttributes[i].bold?1:0);
        labels[i]->SetItalic(labelTextAttributes[i].italic?1:0);

        // Pass the opacity in the line offset.
        labels[i]->SetLineOffset(labelTextAttributes[i].color[3]);
    }
}
